5.02. Функции
Функции
Функция — это именованный блок кода, предназначенный для выполнения определённой задачи. Она может принимать входные данные (аргументы), обрабатывать их и возвращать результат. Функции позволяют организовать код по принципу модульности: повторное использование, изоляция логики, упрощение отладки и тестирования.
В отличие от некоторых других языков программирования, таких как Java или C#, где методы строго связаны с классами и требуют явного указания типов возвращаемого значения и параметров, в Python функции являются автономными конструкциями, не привязанными к классам по умолчанию, а система типов — динамической. Это не означает, что типы отсутствуют, но они проверяются во время выполнения, а не на этапе компиляции.
Функции в Python обладают рядом характерных черт, отличающих их от аналогичных конструкций в строго типизированных языках:
- Динамическая типизация параметров и возвращаемых значений. Типы аргументов и возвращаемых значений не указываются в сигнатуре функции. Проверка корректности типов происходит только во время выполнения.
- Гибкая передача аргументов. Поддерживается комбинированное использование позиционных, именованных аргументов, а также распаковка коллекций через
*argsи словарей через**kwargs. - Функции — объекты первого класса. Функции можно присваивать переменным, передавать как аргументы, возвращать из других функций. Эта особенность фундаментальна для понимания поведения функций в Python, даже если она будет рассмотрена подробнее позже.
- Отсутствие необходимости объявления типа возврата. Ключевое слово return используется для возврата значения, но тип возвращаемого результата не декларируется. Функция может возвращать разные типы в разных ветвях исполнения.
- Автоматическое создание локального пространства имён. При вызове функции создаётся новая область видимости, в которой существуют локальные переменные. Управление доступом к глобальным и нелокальным переменным осуществляется через явные инструкции.
Функция в Python объявляется с помощью ключевого слова def, за которым следует имя функции, список параметров в круглых скобках и двоеточие.
Тело функции представляет собой блок кода, выделенный отступом (обычно 4 пробела).
def greet(name):
return f"Hello, {name}!"
Синтаксис:
def <имя_функции>(<список_параметров>):
<тело функции>
Имя функции должно следовать правилам идентификаторов: начинаться с буквы или подчёркивания, содержать только буквы, цифры и подчёркивания. Рекомендуется использовать змеиный_регистр (snake_case) согласно PEP 8.
Тело функции форматируется с соблюдением отступов. Отступ определяет принадлежность строк к блоку. Все строки с одинаковым отступом считаются частью одного блока. Завершение отступа означает выход из области действия функции.
Параметр — переменная, указанная в сигнатуре функции.
Аргумент — фактическое значение, переданное функции при вызове.
Например, в вызове greet("Alice"), "Alice" — аргумент, а name в def greet(name): — параметр.
Позиционные параметры. Аргументы передаются в порядке, соответствующем порядку параметров в определении функции.
def add(a, b):
return a + b
result = add(3, 5) # a=3, b=5
Именованные параметры (ключевые аргументы). Аргументы передаются с указанием имени параметра. Это позволяет задавать аргументы в произвольном порядке.
result = add(b=5, a=3)
Именованные аргументы могут использоваться только после позиционных.
Параметры со значениями по умолчанию. Параметру может быть присвоено значение по умолчанию. Такой параметр становится необязательным при вызове.
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Bob")) # Hello, Bob!
print(greet("Bob", "Hi")) # Hi, Bob!
Важно: параметры со значениями по умолчанию должны следовать после всех обязательных параметров. Значения по умолчанию вычисляются один раз при определении функции. Не рекомендуется использовать изменяемые объекты (например, списки или словари) в качестве значений по умолчанию.
Параметры до / — только позиционные.
Параметры после * — только именованные.
def func(pos_only, /, standard, *, kwd_only):
pass
Здесь:
pos_onlyможно передать только позиционно.standard— и позиционно, и по имени.kwd_only— только по имени.
Python предоставляет механизм для приёма произвольного числа аргументов.
• *args — собирает все позиционные аргументы в кортеж.
• **kwargs — собирает все именованные аргументы в словарь.
def log_call(*args, **kwargs):
print(f"Позиционные аргументы: {args}")
print(f"Именованные аргументы: {kwargs}")
log_call(1, 2, action="save", user="admin")
# Вывод:
# Позиционные аргументы: (1, 2)
# Именованные аргументы: {'action': 'save', 'user': 'admin'}
Эти механизмы часто используются при создании декораторов, базовых классов, функций-обёрток.
Распаковка аргументов при вызове:
args = [1, 2]
kwargs = {"c": 3, "d": 4}
def func(a, b, c, d):
return a + b + c + d
func(*args, **kwargs) # эквивалентно func(1, 2, c=3, d=4)
Оператор return завершает выполнение функции и возвращает указанное значение вызывающему коду. Если return отсутствует или не содержит значения, функция возвращает None.
def square(x):
return x ** 2
def do_nothing():
pass # вернёт None
result = do_nothing() # result == None
Функция может возвращать несколько значений — на практике это кортеж:
def divide_remainder(a, b):
return a // b, a % b
quotient, remainder = divide_remainder(10, 3)
Это синтаксический сахар для создания и распаковки кортежа.
В Python действует правило LEGB:
- L — Local (локальная область — внутри функции),
- E — Enclosing (объемлющая — вложенная функция),
- G — Global (глобальная — модуль),
- B — Built-in (встроенная — вроде
print,len).
Переменные, объявленные внутри функции, по умолчанию являются локальными.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global
Для изменения глобальной переменной внутри функции используется ключевое слово global:
counter = 0
def increment():
global counter
counter += 1
Для изменения переменной из объемлющей области — nonlocal:
def outer():
x = 1
def inner():
nonlocal x
x += 1
inner()
print(x) # 2
Без nonlocal интерпретатор создаст новую локальную переменную x, не затрагивая внешнюю.
Кроме именованных функций, объявляемых через def, Python поддерживает анонимные функции с помощью выражения lambda.
Синтаксис:
lambda <параметры>: <выражение>
Тело lambda — одно выражение (не блок кода). Не может содержать операторы (return, if, for и т.п.), кроме тернарного оператора. Не может иметь аннотаций типов (хотя это технически возможно, но не поддерживается стандартом).
Примеры:
square = lambda x: x ** 2
add = lambda a, b: a + b
is_even = lambda n: n % 2 == 0
lambda часто используется как аргумент для функций высшего порядка:
data = [(1, 'b'), (2, 'a'), (3, 'c')]
sorted(data, key=lambda x: x[1]) # сортировка по второму элементу
Хотя lambda удобна для кратких случаев, её использование не обязательно. Любая lambda-функция может быть заменена на обычную, определённую через def. Выбор зависит от читаемости и контекста.
Функция называется рекурсивной, если она вызывает саму себя.
Рекурсия — естественный способ решения задач, имеющих рекурсивную структуру (например, обход деревьев, вычисление факториала, последовательности Фибоначчи). Пример:
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
Базовый случай — условие завершения рекурсии. Без него возникает бесконечная рекурсия и переполнение стека вызовов.
Глубина рекурсии в Python ограничена (по умолчанию ~1000). Её можно изменить с помощью sys.setrecursionlimit(), но это не рекомендуется из-за риска аварийного завершения.
Рекурсия может быть менее эффективной, чем итерация, из-за накладных расходов на вызовы. Однако она часто упрощает реализацию алгоритмов на основе разделяй-и-властвуй.
Иногда требуется объявить функцию, тело которой пока не реализовано. Для этого используется оператор-заглушка pass.
def todo():
pass # заглушка, ничего не делает
Также можно использовать ... (Ellipsis), хотя это менее распространено:
def stub():
...
Благодаря динамической типизации, гибкой системе аргументов и возможности возвращать функции, они служат основой для многих парадигм программирования — от процедурной до функциональной.
Функции первого класса
Функции первого класса
Функции как объекты: присваивание, передача, хранение.
Замыкания (closures): вложенные функции, захват переменных.
Декораторы: Что такое и зачем нужны.
Синтаксис @decorator.
Примеры: логирование, кэширование, проверка прав.
Декораторы с параметрами.
Применение: улучшение читаемости и повторного использования.
Термин «объект первого класса» (first-class object) происходит из теории языков программирования и означает сущность, которая:
- Может быть присвоена переменной.
- Может быть передана как аргумент другой функции.
- Может быть возвращена из функции.
- Может быть сохранена в структурах данных (списках, словарях, множествах и т.п.).
Если такой объект — функция, то говорят: функция является объектом первого класса. В Python все функции удовлетворяют этим критериям. Это не просто синтаксическая конструкция — это полноценные объекты, существующие в runtime, обладающие типом, атрибутами и поведением, как и любые другие данные. Важно: термин «первого класса» не означает «лучше» или «высокопроизводительнее». Он указывает на степень интеграции функций в систему типов и выполнения программы.
Например, в C функции нельзя напрямую хранить в списках или возвращать из других функций без использования указателей (что выходит за рамки базовой модели языка). В Java до версии 8 методы не были объектами первого класса — их можно было использовать только через интерфейсы или рефлексию.
В Python же функция — полноценный объект, создаваемый во время исполнения.
Поскольку функции в Python — объекты, они могут использоваться так же, как числа, строки или списки.
Присваивание функции переменной:
def greet(name):
return f"Hello, {name}!"
say_hello = greet # Присваиваем функцию переменной
print(say_hello("Alice")) # Вызов через новое имя
Здесь greet и say_hello — два имени, ссылающиеся на один и тот же объект-функцию.
Передача функции как аргумента:
def apply_operation(func, x):
return func(x)
def square(n):
return n ** 2
result = apply_operation(square, 5) # 25
Такой подход лежит в основе функций высшего порядка — функций, которые принимают или возвращают другие функции.
Хранение функций в структурах данных:
operations = {
'square': lambda x: x ** 2,
'double': lambda x: x * 2,
'negate': lambda x: -x
}
result = operations['square'](4) # 16
Это позволяет динамически выбирать поведение программы.
Проверка типа и атрибуты функции.
Функция — это объект типа function. Его можно исследовать:
print(type(greet)) # <class 'function'>
print(callable(greet)) # True
print(greet.__name__) # 'greet'
print(greet.__module__) # имя модуля
Атрибуты функций можно расширять:
greet.author = "John Doe"
print(greet.author) # John Doe
Замыкание — это функция, которая «захватывает» переменные из своей объемлющей области видимости и продолжает иметь к ним доступ даже после завершения этой области.
Условия возникновения замыкания - наличие вложенной функции; вложенная функция ссылается на переменную из внешней функции; внешняя функция возвращает внутреннюю.
def make_multiplier(factor):
def multiply(x):
return x * factor # захватывает factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
Здесь multiply — замыкание. Оно «помнит» значение factor, даже когда make_multiplier уже завершилась.
Когда функция возвращается, вместе с ней возвращается и ссылка на её окружение (cell objects), в котором хранятся захваченные переменные. Это обеспечивает инкапсуляцию состояния без использования классов. Проверить наличие замыкания можно через атрибут __closure__:
print(double.__closure__) # (<cell at 0x...: int object at 0x...>,)
print(double.__closure__[0].cell_contents) # 2
Пример - счётчик:
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
Декоратор — это функция, которая принимает другую функцию и возвращает новую функцию, расширяя или изменяя её поведение без модификации исходного кода. Декораторы реализуют принцип открытости/закрытости: объект (функция) открыт для расширения, но закрыт для изменения. Они позволяют вынести побочные эффекты (логирование, проверку прав, кэширование) в отдельные модули, улучшая читаемость и повторное использование.
Синтаксис @decorator. Без декоратора:
def my_function():
print("Основная логика")
def logger(func):
def wrapper():
print(f"Вызов: {func.__name__}")
func()
print("Вызов завершён")
return wrapper
my_function = logger(my_function)
С синтаксисом @:
@logger
def my_function():
print("Основная логика")
Оба варианта эквивалентны. Декоратор применяется при определении функции.
Декоратор с параметром — это функция, возвращающая декоратор. То есть трёхуровневая структура:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello():
print("Привет!")
Здесь repeat(3) возвращает декоратор. Декоратор применяется к say_hello.